package cn.edu.klmy.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author 顾伟
 *         创建日期：13-2-3
 */
@Component("dataSource")
public class DataSourceFactoryBean implements FactoryBean<DataSource>, InitializingBean {
    private final Logger logger = LoggerFactory.getLogger(DataSourceFactoryBean.class);

    private DataSource dataSource;

    /**
     * 数据库登录用户名
     */
    @Value("#{dataSourceProperties['jdbcUserName']}")
    private String userName = "";

    /**
     * 数据库登录密码
     */
    @Value("#{dataSourceProperties['jdbcPassword']}")
    private String password;
    /**
     * JDBC驱动名称
     */
    @Value("#{dataSourceProperties['jdbcDriverClassName']}")
    private String driverClassName;
    /**
     * JDBC连接字符串
     */
    @Value("#{dataSourceProperties['jdbcUrl']}")
    private String jdbcUrl;
    /**
     * 数据连接池名称
     */
    @Value("#{dataSourceProperties['dataSourceName']}")
    private String name;
    /**
     * 数据连接池属性
     */
    @Value("#{dataSourceProperties}")
    private Properties properties;

    @Override
    public DataSource getObject() throws Exception {
        return dataSource;
    }

    @Override
    public Class<?> getObjectType() {
        return DataSource.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.hasText(driverClassName, "数据源连接池驱动未定义！");
        Assert.hasText(jdbcUrl, "数据源连接字符串未定义！");
        Assert.hasText(name, "数据源连接池名称未定义！");
        Assert.hasText(password, "数据源连接池连接密码未定义！");
        Assert.hasText(userName, "数据源连接池连接用户未定义！");

        DruidDataSource druidDataSource = new DruidDataSource();

        // 配置连接属性
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(jdbcUrl);
        druidDataSource.setUsername(userName);
        druidDataSource.setPassword(password);

        druidDataSource.setDefaultCatalog(properties.getProperty(
                "defaultCatalog", null));// 数据库连接池连接分类
        druidDataSource.setName(name);// 设置连接池名称
        druidDataSource.setConnectionProperties(properties.getProperty(
                "connectionProperties", null));// 设置连接属性，多个属性以";"分隔

        // 配置连接池主要属性
        druidDataSource.setInitialSize(Integer.parseInt(properties.getProperty(
                "initialSize", "0")));// 连接池最小连接数
        druidDataSource.setMaxActive(Integer.parseInt(properties.getProperty(
                "maxActive", "8")));// 连接池同一时间保持活动的最大连接数，如果负数则没有限制
        druidDataSource.setMinIdle(Integer.parseInt(properties.getProperty(
                "minIdle", "0")));// 连接池保持最小的空闲连接数
//        druidDataSource.setMaxIdle(Integer.parseInt(properties.getProperty(
//                "maxIdle", "8")));// 连接池保持最大的空闲连接数，如果负数则没有限制
        druidDataSource.setMaxWait(Long.parseLong(properties.getProperty(
                "maxWait", "-1")));// 连接等待超时的时间，单位毫秒，如果负数则不限制等待时间
        druidDataSource.setMinEvictableIdleTimeMillis(Long.parseLong(properties
                .getProperty("minEvictableIdleTimeMillis", "1800000")));// 配置一个连接在池中最小生存的时间，单位是毫秒

        // 配置连接池检测属性
        druidDataSource.setValidationQuery(properties.getProperty(
                "validationQuery", null));// 验证连接正确性SQL语句
        druidDataSource.setValidationQueryTimeout(Integer.parseInt(properties
                .getProperty("validationQueryTimeout", "0")));// 测试有效的超时时间
        druidDataSource.setTestOnBorrow(Boolean.parseBoolean(properties
                .getProperty("testOnBorrow", "true")));// 在获取时测试有效性
        druidDataSource.setTestOnReturn(Boolean.parseBoolean(properties
                .getProperty("testOnReturn", "false")));// 在归还时测试有效性
        druidDataSource.setTestWhileIdle(Boolean.parseBoolean(properties
                .getProperty("testWhileIdle", "false")));// 空闲时测试有效性
        druidDataSource.setTimeBetweenEvictionRunsMillis(Long
                .parseLong(properties.getProperty(
                        "timeBetweenEvictionRunsMillis", "-1")));// 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒
        druidDataSource.setTimeBetweenConnectErrorMillis(Long
                .parseLong(properties.getProperty(
                        "timeBetweenConnectErrorMillis", "30000")));// 配置间隔多久才进行一次检测，检测需要关闭的出错连接，单位是毫秒

        // 配置连接池性能参数
        druidDataSource.setOracle(Boolean.parseBoolean(properties.getProperty(
                "isOracle", "false")));// 设置是否用于Oracle连接，该数据池对Oracle有优化
        druidDataSource.setPoolPreparedStatements(Boolean
                .parseBoolean(properties.getProperty("poolPreparedStatements",
                        "false")));// 开启PreparedStatement缓存，多用于Oracle数据库
        druidDataSource.setSharePreparedStatements(Boolean
                .parseBoolean(properties.getProperty("sharePreparedStatements",
                        "false")));// 开启PreparedStatement共享
        druidDataSource.setUseOracleImplicitCache(Boolean
                .parseBoolean(properties.getProperty("useOracleImplicitCache",
                        "true")));// 开启Oracle隐式缓存
        druidDataSource.setMaxOpenPreparedStatements(Integer
                .parseInt(properties.getProperty("maxOpenPreparedStatements",
                        "10")));// 缓存最大PreparedStatement数，0代表无限

        // 配置连接池连接事务属性
        druidDataSource.setDefaultAutoCommit(Boolean.parseBoolean(properties
                .getProperty("defaultAutoCommit", "true")));// 是否自动提交
        druidDataSource.setDefaultReadOnly(Boolean.valueOf(properties
                .getProperty("defaultReadOnly", "false")));// 设置连接只读属性
        String value = properties.getProperty("defaultTransactionIsolation",
                "SERIALIZABLE");
        int level = -1;
        if ("NONE".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_NONE;
        } else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_READ_COMMITTED;
        } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_READ_UNCOMMITTED;
        } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_REPEATABLE_READ;
        } else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
            level = Connection.TRANSACTION_SERIALIZABLE;
        } else {
            try {
                level = Integer.parseInt(value);
            } catch (NumberFormatException e) {
                level = -1;
            }
        }
        druidDataSource.setDefaultTransactionIsolation(level);// 设置连接池连接的事务隔离级别

        // 配置数据池监控
        try {
            druidDataSource.setFilters(properties.getProperty("filters", null));// 配置监控统计拦截的filters
        } catch (SQLException e) {            /*-异常处理*/
        }

        this.dataSource = druidDataSource;
    }
}
